package com.foodchain.util;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.digest.DigestUtils;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.FileInputStream;
import java.io.InputStream;
import java.lang.Character.UnicodeBlock;
import java.math.BigDecimal;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.text.DateFormat;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public final class Utils {

	private Utils() {
	}

	private static final String DF_YMD = "yyyy-MM-dd";

	private static final String DF_YMD_HMS = "yyyy-MM-dd HH:mm:ss";

	private static final String DF_FILE = "yyyyMMddHHmmss";
	
	private static final String DF_FILE_DATE = "yyyyMMdd";

	private static final String[] HEX_DIGITS = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F" };

	public static String uuid() {
		return UUID.randomUUID().toString().replace("-", "");
	}

	public static boolean isEmpty(Object obj) {
		return (null == obj || String.valueOf(obj).trim().length() == 0);
	}
	
	public static boolean isNotEmpty(Object obj) {
		return !isEmpty(obj);
	}

	public static String firstUpper(String src) {
		if (isEmpty(src)) {
			return src;
		}
		return new String(src.substring(0, 1).toUpperCase() + src.substring(1));
	}

	public static String parseNull(String src) {
		return isEmpty(src) ? "" : String.valueOf(src);
	}

	/**
	 * 首字母小写
	 * 
	 * @param src
	 * @return
	 */
	public static String firstLower(String src) {
		if (isEmpty(src)) {
			return "";
		}
		return new String(src.substring(0, 1).toLowerCase() + src.substring(1));
	}

	/**
	 * 格式化日期为YYYY-MM-DD形式的字符串。 如果参数为空，则返回当前时间
	 * 
	 * @param date
	 *            日期
	 * @return 格式化的日期字符串
	 */
	public static String getDefDate(Date date) {
		if (null == date) {
			date = new Date(System.currentTimeMillis());
		}
		DateFormat df = new SimpleDateFormat(DF_YMD);
		return df.format(date);
	}

	/**
	 * 格式化日期为YYYY-MM-DD HH:MM:SS形式的字符串。 如果参数为空，则返回当前时间
	 * 
	 * @param date
	 *            日期
	 * @return 格式化的日期字符串
	 */
	public static String getDefDateTime(Date date) {
		if (null == date) {
			date = new Date(System.currentTimeMillis());
		}
		DateFormat df = new SimpleDateFormat(DF_YMD_HMS);
		return df.format(date);
	}

	/**
	 * 格式化日期为YYYY-MM-DD形式的字符串。 如果参数为空，则返回空字符串
	 * 
	 * @param date
	 *            日期
	 * @return 格式化的日期字符串
	 */
	public static String getDate(Date date) {
		if (null == date) {
			return "";
		}
		DateFormat df = new SimpleDateFormat(DF_YMD);
		return df.format(date);
	}

	public static String getDate() {
		return getDefDate(null);
	}

	public static String getDateTime() {
		return getDefDateTime(null);
	}

	public static String getFileDateTime() {
		DateFormat df = new SimpleDateFormat(DF_FILE);
		return df.format(new Date());
	}
	
	public static String getFileDate() {
		DateFormat df = new SimpleDateFormat(DF_FILE_DATE);
		return df.format(new Date());
	}

	/**
	 * {@link Date}转换为{@link java.sql.Date}
	 * 
	 * @param date
	 * @return
	 */
	public static java.sql.Date getSqlDate(Date date) {
		if (null == date) {
			return null;
		}
		return new java.sql.Date(date.getTime());
	}

	/**
	 * {@link java.sql.Date}转换为{@link Date}
	 * 
	 * @param date
	 * @return
	 */
	public static Date getUtilDate(java.sql.Date date) {
		if (null == date) {
			return null;
		}
		return new Date(date.getTime());
	}

	public static Date getDate(String dateString, String dateFormat) {
		if (null == dateString) {
			return null;
		}
		DateFormat df = new SimpleDateFormat(dateFormat);
		try {
			return df.parse(dateString);
		} catch (Exception e) {
			System.out.println("日期转换错误，输入日期：" + dateString + "，转换格式：" + dateFormat);
			e.printStackTrace();
			return null;
		}
	}

	public static String toStr(Object val) {
		if (null == val) {
			return "";
		}
		if (val instanceof Date) {
			return getDate((Date) val);
		}
		return String.valueOf(val);
	}

	@SuppressWarnings({ "rawtypes" })
	public static Object toType(Class clazz, Object val) throws Exception {
		if (null == clazz || null == val) {
			return null;
		}

		Object obj = null;
		if (String.class == clazz) { // String
			obj = String.valueOf(val);
		} else if (Integer.class == clazz) { // int
			obj = Integer.valueOf(String.valueOf(val));
		} else if (int.class == clazz) {
			obj = (int) Integer.valueOf(String.valueOf(val));
		} else if (Double.class == clazz) { // double
			obj = Double.valueOf(String.valueOf(val));
		} else if (double.class == clazz) {
			obj = (double) Double.valueOf(String.valueOf(val));
		} else if (Float.class == clazz) { // float
			obj = Float.valueOf(String.valueOf(val));
		} else if (float.class == clazz) {
			obj = (float) Float.valueOf(String.valueOf(val));
		} else if (Long.class == clazz) { // long
			obj = Long.valueOf(String.valueOf(val));
		} else if (long.class == clazz) {
			obj = (long) Long.valueOf(String.valueOf(val));
		} else if (Byte.class == clazz) { // byte
			obj = Byte.valueOf(String.valueOf(val));
		} else if (byte.class == clazz) {
			obj = (byte) Byte.valueOf(String.valueOf(val));
		} else if (Date.class == clazz) { // Date
			obj = try2Date(val);
		} else if (boolean.class == clazz) {
			String str = String.valueOf(val).trim().toUpperCase();
			obj = ("TRUE".equals(str) || "1".equals(str) || "YES".equals(str) || "ON".equals(str)) ? true : false;
		} else if (Boolean.class == clazz) {
			String str = String.valueOf(val).trim().toUpperCase();
			obj = ("TRUE".equals(str) || "1".equals(str) || "YES".equals(str) || "ON".equals(str)) ? Boolean.TRUE : Boolean.FALSE;
		} else if (BigDecimal.class == clazz) {
			String str = String.valueOf(val).trim().toUpperCase();
			if (Utils.isFloat(str) || Utils.isNumeric(str)) {
				obj = new BigDecimal(str);
			}
		} else {
			obj = val;
		}

		return obj;
	}

	private static Date try2Date(Object obj) {
		if (null == obj) {
			return null;
		}
		Date date = null;
		if (obj instanceof String) {
			String str = (String) obj;
			if (8 == str.length()) {
				date = getDate(str, "yyyyMMdd");
			} else if (10 == str.length()) {
				if (str.contains("/")) {
					date = getDate(str, "yyyy/MM/dd");
				} else if (str.contains("-")) {
					date = getDate(str, "yyyy-MM-dd");
				}
			} else {
				if (str.contains("/")) {
					date = getDate(str, "yyyy/MM/dd HH:mm:ss");
				} else if (str.contains("-")) {
					date = getDate(str, "yyyy-MM-dd HH:mm:ss");
				}
			}
		}
		if (null == date) {
			String str = String.valueOf(obj).trim();
			if (Utils.isNumeric(str)) {
				if (str.length() == 10) {
					date = new Date(Long.parseLong(str) * 1000);
				} else if (str.length() == 13) {
					date = new Date(Long.parseLong(str));
				}
			}
		}
		return date;
	}

	/**
	 * 数组转换成List
	 * 
	 * @param obj
	 * @return
	 */
	public static List<Object> asList(Object obj) {
		List<Object> result = new ArrayList<Object>();
		Object[] objs = asArray(obj);
		if (null != objs && objs.length > 0) {
			for (int i = 0; i < objs.length; ++i) {
				result.add(objs[i]);
			}
		}
		return result;
	}

	/**
	 * 数组转换成Object数组，不支持基本类型
	 * 
	 * @param obj
	 * @return
	 */
	public static Object[] asArray(Object obj) {
		Object[] results = null;
		if (obj != null && obj.getClass().isArray()) {
			if (obj.getClass() == String[].class) {
				String[] objs = (String[]) obj;
				results = new Object[objs.length];
				System.arraycopy(objs, 0, results, 0, objs.length);
			} else if (obj.getClass() == Byte[].class) {
				Byte[] objs = (Byte[]) obj;
				results = new Object[objs.length];
				System.arraycopy(objs, 0, results, 0, objs.length);
			} else if (obj.getClass() == Long[].class) {
				Long[] objs = (Long[]) obj;
				results = new Object[objs.length];
				System.arraycopy(objs, 0, results, 0, objs.length);
			} else if (obj.getClass() == Integer[].class) {
				Integer[] objs = (Integer[]) obj;
				results = new Object[objs.length];
				System.arraycopy(objs, 0, results, 0, objs.length);
			} else if (obj.getClass() == Double[].class) {
				Double[] objs = (Double[]) obj;
				results = new Object[objs.length];
				System.arraycopy(objs, 0, results, 0, objs.length);
			} else if (obj.getClass() == Float[].class) {
				Float[] objs = (Float[]) obj;
				results = new Object[objs.length];
				System.arraycopy(objs, 0, results, 0, objs.length);
			}
		}
		return results;
	}

	/**
	 * 转换字节数组为16进制字串
	 * 
	 * @param bytes
	 *            字节数组
	 * @return 16进制字串
	 */
	public static String bytes2Hex(byte[] bytes) {
		StringBuffer resultSb = new StringBuffer();
		for (int i = 0; i < bytes.length; i++) {
			resultSb.append(byte2Hex(bytes[i]));
		}
		return resultSb.toString();
	}

	private static String byte2Hex(byte b) {
		int n = b < 0 ? 256 + b : b;
		return HEX_DIGITS[n / 16] + HEX_DIGITS[n % 16];
	}

	/**
	 * 
	 * 十六进制转换字符串
	 */
	public static String hexStr2Str(String hexStr) {
		String str = "0123456789ABCDEF";
		char[] hexs = hexStr.toCharArray();
		byte[] bytes = new byte[hexStr.length() / 2];
		int n;
		for (int i = 0; i < bytes.length; i++) {
			n = str.indexOf(hexs[2 * i]) * 16;
			n += str.indexOf(hexs[2 * i + 1]);
			bytes[i] = (byte) (n & 0xff);
		}
		String result = "";
		try {
			result = new String(bytes, "UTF-8");
		} catch (Exception e) {
		}
		return result;
	}

	public static String randHex() {
		Random random = new Random();
		int i = random.nextInt(16);
		return HEX_DIGITS[i];
	}

	public static final <T> Object convert2Type(String val, Class<T> clazz) throws Exception {
		if (isEmpty(val)) {
			return null;
		}
		if (clazz == String.class) {
			return val;
		}
		if (clazz == int.class || clazz == Integer.class) {
			return Integer.valueOf(val);
		}
		if (clazz == long.class || clazz == Long.class) {
			return Long.valueOf(val);
		}
		if (clazz == boolean.class || clazz == Boolean.class) {
			return Boolean.valueOf(val.toLowerCase());
		}
		if (clazz.isEnum()) {
			T[] ins = clazz.getEnumConstants();
			for (T t : ins) {
				Enum<?> em = (Enum<?>) t;
				if (em.toString().equals(val) || (isNumeric(val) && em.ordinal() == Integer.valueOf(val))) {
					return t;
				}
			}
		}
		if (clazz == Date.class) {
			return getDateFromString(val);
		}
		if (clazz == double.class || clazz == Double.class) {
			return Double.valueOf(val);
		}
		if (clazz == float.class || clazz == Float.class) {
			return Float.valueOf(val);
		}
		if (clazz == byte.class || clazz == Byte.class) {
			return Byte.valueOf(val);
		}
		if (clazz == short.class || clazz == Short.class) {
			return Short.valueOf(val);
		}
		if (clazz == char.class || clazz == Character.class) {
			return val.charAt(0);
		}
		if (clazz == BigDecimal.class) {
			return new BigDecimal(val);
		}
		return null;
	}

	public static boolean isNumeric(String str) {
		if (isEmpty(str)) {
			return false;
		}
		return Pattern.matches("[0-9]*", str);
	}

	/**
	 * MD5加密(信息加密算法)
	 * 
	 * @param message
	 *            要进行MD5加密的字符串
	 * @return 加密结果为32位字符串
	 */
	public static String getMD5(String message, boolean isUpperCase) {
		return isUpperCase ? getMD5(message).toUpperCase() : getMD5(message);
	}

	/**
	 * java语言MD5加密
	 * 
	 * @param message
	 */
	public static String getMD5(String message) {
		try {
			MessageDigest md5 = MessageDigest.getInstance("MD5");
			md5.update(message.getBytes("UTF-8"));
			return bytes2Hex(md5.digest()).toLowerCase();
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	public static String formatAmount(BigDecimal amount) {
		return formatDouble(amount, 8);
	}

	public static String formatAmount(Double amount) {
		return formatDouble(amount, 8);
	}

	public static String formatDouble(Object obj, int scale) {
		return String.format("%." + scale + "f", obj);
	}

	public static BigDecimal toBig(Object o) {
		if (o == null || o.toString().equals("") || o.toString().equals("NaN")) {
			return new BigDecimal(0);
		}
		return new BigDecimal(o.toString());
	}

	public static String getPercent(Object divisor, Object dividend) {
		if (divisor == null || dividend == null) {
			return "";
		}
		NumberFormat percent = NumberFormat.getPercentInstance();
		// 建立百分比格式化引用
		percent.setMaximumFractionDigits(2);
		BigDecimal a = toBig(divisor);
		BigDecimal b = toBig(dividend);
		if (a.equals(toBig(0)) || b.equals(toBig(0)) || a.equals(toBig(0.0)) || b.equals(toBig(0.0))) {
			return "0.00%";
		}
		BigDecimal c = a.divide(b, 4, BigDecimal.ROUND_DOWN);
		return percent.format(c);
	}

	public static String divideNumber(Object divisor, Object dividend) {
		if (divisor == null || dividend == null) {
			return "";
		}
		BigDecimal a = toBig(divisor);
		BigDecimal b = toBig(dividend);
		if (a.equals(toBig(0)) || b.equals(toBig(0))) {
			return "0";
		}
		BigDecimal c = a.divide(b, 2, BigDecimal.ROUND_DOWN);
		return c.toString();
	}

	public static String getFileMD5(String fileName) {
		InputStream is = null;
		try {
			is = new FileInputStream(fileName);
			return DigestUtils.md5Hex(is);
		} catch (Exception e) {
			throw new RuntimeException(e);
		} finally {
			if (null != is) {
				try {
					is.close();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
	}

	public static String renderSize(long size) {
		if (size <= 0) {
			return "0B";
		}
		String units[] = { "B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" };
		int index = (int) Math.floor(Math.log(size) / Math.log(1024));
		double renderSize = size / Math.pow(1024, index);
		return formatDouble(renderSize, 2) + units[index];
	}

	/**
	 * AES 加密
	 * @param src 加密的字符串
	 * @param key 32位密钥
	 * @return Base64字符串，经过URLEncod编码
	 */
	public static String AESEncryptCBC(String src, String key) {
		try {
			String iv = key.substring(0, 16);
			key = key.substring(16);
			Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); //"算法/模式/补码方式"
            int blockSize = cipher.getBlockSize();

            byte[] dataBytes = src.getBytes("UTF-8");
            int plaintextLength = dataBytes.length;
            if (plaintextLength % blockSize != 0) {
                plaintextLength = plaintextLength + (blockSize - (plaintextLength % blockSize));
            }

            byte[] content = new byte[plaintextLength];
            System.arraycopy(dataBytes, 0, content, 0, dataBytes.length);

            SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES");
            IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes());

            cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec);
			
			return URLEncoder.encode(Base64.encodeBase64String(cipher.doFinal(content)), "UTF-8");
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	public static String AESDecryptCBC(String src, String key) {
		try {
			String iv = key.substring(0, 16);
			key = key.substring(16);
			byte[] content = Base64.decodeBase64(URLDecoder.decode(src, "UTF-8"));
			
			Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
            SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES");
            IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes());
            cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec);
            
			return new String(cipher.doFinal(content), "UTF-8");
		} catch (Exception e) {
			e.printStackTrace();
		}

		return null;
	}
	
	public static byte[] hexStr2Bytes(String hexStr) {
		if (hexStr.length() < 1) {
			return null;
		}
		
		byte[] result = new byte[hexStr.length() / 2];
		for (int i = 0; i < hexStr.length() / 2; i++) {
			int high = Integer.parseInt(hexStr.substring(i * 2, i * 2 + 1), 16);
			int low = Integer.parseInt(hexStr.substring(i * 2 + 1, i * 2 + 2), 16);
			result[i] = (byte) (high * 16 + low);
		}
		
		return result;
	}
	
	/**
	 * 除去数组中的空值和签名参数
	 * 
	 * @param sArray
	 *            签名参数组
	 * @return 去掉空值与签名参数后的新签名参数组
	 */
	public static Map<String, String> paraFilter(Map<String, String> sArray) {

		Map<String, String> result = new HashMap<String, String>();

		if (sArray == null || sArray.size() <= 0) {
			return result;
		}

		for (String key : sArray.keySet()) {
			String value = sArray.get(key);
			if (value == null || value.equals("") || key.equalsIgnoreCase("sign") || key.equalsIgnoreCase("sign_type")) {
				continue;
			}
			result.put(key, value);
		}

		return result;
	}

	/**
	 * 把数组所有元素排序，并按照“参数=参数值”的模式用“&”字符拼接成字符串
	 * 
	 * @param params
	 *            需要排序并参与字符拼接的参数组
	 * @return 拼接后字符串
	 */
	public static String createLinkString(Map<String, String> params) {

		List<String> keys = new ArrayList<String>(params.keySet());
		Collections.sort(keys);

		String prestr = "";

		for (int i = 0; i < keys.size(); i++) {
			String key = keys.get(i);
			String value = params.get(key);

			if (i == keys.size() - 1) {// 拼接时，不包括最后一个&字符
				prestr = prestr + key + "=" + value;
			} else {
				prestr = prestr + key + "=" + value + "&";
			}
		}

		return prestr;
	}
	
	public static String createJsonString(Map<String, String> params) {
		List<String> keys = new ArrayList<String>(params.keySet());
		Collections.sort(keys);

		String prestr = "{";

		for (int i = 0; i < keys.size(); i++) {
			String key = keys.get(i);
			String value = params.get(key);

			if (i == keys.size() - 1) {// 拼接时，不包括最后一个&字符
				prestr = prestr + key + ":\"" + value + "\"";
			} else {
				prestr = prestr + key + ":\"" + value + "\", ";
			}
		}
		
		prestr = prestr + "}";
		return prestr;
	}
	
	public static boolean isFloat(String str) {
		if (isNumeric(str)) {
			return true;
		}
		return Pattern.matches("[0-9]+.[0-9]+", str);
	}
	
	public static Date getDateFromString(String val) {
		Date date = getDateFromString(val, "yyyy-MM-dd HH:mm:ss");
		if (null == date) {
			date = getDateFromString(val, "yyyy-MM-dd");
		}
		if (null == date) {
			date = getDateFromString(val, "yyyy-MM-dd HH:mm");
		}
		return date;
	}
	
	public static Date getDateFromString(String val, String pattern) {
		try {
			return new SimpleDateFormat(pattern).parse(val);
		} catch (Exception e) {
			
		}
		return null;
	}

	/**
	 * UTF-8字符串转Unicode编码
	 * @param src
	 * @return
	 */
	public static String utf8ToUnicode(String src) {
		char[] myBuffer = src.toCharArray();
		StringBuffer dest = new StringBuffer();
		for (int i = 0; i < src.length(); i++) {
			UnicodeBlock ub = UnicodeBlock.of(myBuffer[i]);
			if (ub == UnicodeBlock.BASIC_LATIN) { // 英文及数字等
				dest.append(myBuffer[i]);
			} else if (ub == UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS) { // 全角半角字符
				int j = (int) myBuffer[i] - 65248;
				dest.append((char) j);
			} else { // 汉字
				int s = (int) myBuffer[i];
				String hexS = Integer.toHexString(s);
				String unicode = "\\u" + hexS;
				dest.append(unicode.toLowerCase());
			}
		}
		return dest.toString();
	}
	
	/**
	 * UTF-8字符串转Unicode编码
	 * @param src
	 * @return
	 */
	public static String unicodeToUtf8(String src) {
		Charset set = Charset.forName("UTF-16");
		Pattern p = Pattern.compile("\\\\u([0-9a-fA-F]{4})");
		Matcher m = p.matcher(src);
		int start = 0;
		int start2 = 0;
		StringBuffer dest = new StringBuffer();
		while (m.find(start)) {
			start2 = m.start();
			if (start2 > start) {
				String seg = src.substring(start, start2);
				dest.append(seg);
			}
			String code = m.group(1);
			int i = Integer.valueOf(code, 16);
			byte[] bb = new byte[4];
			bb[0] = (byte) ((i >> 8) & 0xFF);
			bb[1] = (byte) (i & 0xFF);
			ByteBuffer b = ByteBuffer.wrap(bb);
			dest.append(String.valueOf(set.decode(b)).trim());
			start = m.end();
		}
		start2 = src.length();
		if (start2 > start) {
			String seg = src.substring(start, start2);
			dest.append(seg);
		}
		return dest.toString();
	}
	
	public static String getDateTimeCN(Date date) {
		if (null == date) {
			date = new Date();
		}
		DateFormat df = new SimpleDateFormat("yyyy年MM月dd日 HH时mm分ss秒");
		return df.format(date);
	}
	
	public static String getDateCN(Date date) {
		if (null == date) {
			date = new Date();
		}
		DateFormat df = new SimpleDateFormat("yyyy年MM月dd日");
		return df.format(date);
	}
	
	public static boolean isAvlColl(Collection<?> c) {
		return (null != c && c.size() > 0);
	}
	
	/**
	 * 获取当前日期所在月份的最后一天
	 * @param date
	 * @return
	 * @throws Exception
	 */
	public static Calendar getLastDateOfMonth(Date date) throws Exception {
		if (null == date) {
			return null;
		}
		
		Calendar cl = Calendar.getInstance();
		cl.setTime(date);
		cl.add(Calendar.MONTH, 1);
		cl.set(Calendar.DAY_OF_MONTH, 0);
		
		SimpleDateFormat ft1 = new SimpleDateFormat("yyyy-MM-dd");
		SimpleDateFormat ft = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		cl.setTime(ft.parse(ft1.format(cl.getTime()) + " 24:00:00"));
		
		return cl;
	}

}
